package com.nulldev.util.concurrency.threadIt.v4.emapi.uft;

import java.util.List;
import java.util.concurrent.TimeUnit;

import com.nulldev.util.concurrency.threadIt.v3.extensions.TaskStatus;
import com.nulldev.util.concurrency.threadIt.v4.asyncTasks.AsyncTaskCallback;
import com.nulldev.util.concurrency.threadIt.v4.emapi.ExecutorManager;
import com.nulldev.util.concurrency.threadIt.v4.emapi.extensions.IFutureTaskFlags;
import com.nulldev.util.concurrency.threadIt.v4.utils.impls.TEConditions;

public interface Executable<T> extends IFutureTaskFlags {

	/**
	 * Runs the task as a standard task, providing the thread that it is being run
	 * on.
	 * 
	 * @see Executable.run_st()
	 * @param currentThread
	 */
	public default void run(final Thread currentThread) {
		this.run_st();
	}

	/**
	 * Runs the task as a standard task. (st)
	 * 
	 * @returns T or null.
	 * @apiNote This method should be used instead of run().
	 */
	public T run_st();

	/**
	 * Waits until the task is complete and returns the result.
	 * 
	 * @returns T or null.
	 * @throws Throwable on <code>FAILED</code>.
	 */
	public T waitTillCompletion();

	/**
	 * Waits until the task is complete and returns the result <b>WITHIN</b> a
	 * provided time-slice.
	 * 
	 * @returns T or null.
	 * @param amount
	 * @param millis
	 * @throws Throwable-based exception on <code>FAILED</code> or time-out.
	 */
	public T waitFor(final long amount, final TimeUnit millis);

	/**
	 * Adds a callback task if non-null.
	 * 
	 * @param asyncTaskCallback
	 * @returns boolean [true = added to task]
	 */
	public boolean addCallbackTask(final AsyncTaskCallback<T> asyncTaskCallback);

	/**
	 * Same as addCallbackTask(ASyncTaskCallback) but returns task for code
	 * formatting purposes.
	 * 
	 * @param asyncTaskCallback
	 * @returns Task via <code>this</code>
	 */
	public default Executable<T> addCallback(final AsyncTaskCallback<T> asyncTaskCallback) {
		TEConditions.validTaskCallback(asyncTaskCallback);
		this.addCallbackTask(asyncTaskCallback);
		return this;
	}

	/**
	 * Returns the current result object held internally within the task.
	 * 
	 * @returns T or null if the task hasn't completed.
	 */
	public T result();

	/**
	 * Returns a boolean value if the task has completed successfully.
	 * 
	 * @returns boolean [true = completed]
	 * @implNote This check is <b>NOT</b> based upon
	 *           <code>this.status() == TaskStatus.COMPLETED</code>
	 */
	public boolean completed();

	/**
	 * Kills a <code>RUNNING</code> task by interrupting the executing thread.
	 */
	public void kill();

	/**
	 * Returns the status of a task.
	 * 
	 * @returns TaskStatus
	 */
	public TaskStatus status();

	/**
	 * Signals to a task that the executor has placed the task into it's internal
	 * queues. <br>
	 * <b>NOTE:</b> This method should only be used by executors.
	 */
	public void signalQueued();

	/**
	 * Signals to a task that the executor has placed the task into it's internal
	 * queues. <br>
	 * This variant is asynchronous. <br>
	 * <b>NOTE:</b> This method should only be used by executors.
	 */
	public default void asyncSignalQueued() {
		ExecutorManager.globalAsyncSubmit(() -> {
			Executable.this.signalQueued();
			return null;
		});
	}

	/**
	 * Signals to a task that the executor couldn't place the task into it's
	 * internal queues. <br>
	 * <b>NOTE:</b> This method should only be used by executors.
	 */
	public default void rejectSubmission() {
	};

	/**
	 * Returns the equivalent of <code>this.status() == TaskStatus.FAILED</code>
	 * 
	 * @returns boolean
	 */
	public default boolean hasFailed() {
		return this.status() == TaskStatus.FAILED;
	}

	/**
	 * Returns a Throwable of the error that the task has encountered.
	 * 
	 * @returns Throwable or null.
	 */
	public default Throwable getException() {
		return null;
	}

	/**
	 * Returns the equivalent of <code>this.status() == TaskStatus.RUNNING</code>
	 * 
	 * @returns boolean
	 */
	public default boolean isRunning() {
		return this.status() == TaskStatus.RUNNING;
	}

	/**
	 * Returns a typically read-only list of callbacks within a task.
	 * 
	 * @returns List
	 */
	public List<AsyncTaskCallback<?>> callbacks();

	/* FT 1.1 */

	/**
	 * Attempts to cancel a <code>QUEUED</code> or <code>RUNNING</code> task.
	 * 
	 * @since FutureTask 1.1
	 * @returns boolean [true = cancelled]
	 */
	public default boolean requestCancel() {
		return false;
	}

	/**
	 * Attempts to reset a task which has either been killed, cancelled, completed
	 * or failed.
	 * 
	 * @since FutureTask 1.1
	 * @returns boolean [true = reset]
	 */
	public default boolean requestReset() {
		return false;
	}

	/**
	 * Attempts to return a trace of the task invocation/creation.
	 * 
	 * @since FutureTask 1.1
	 * @returns Throwable or NULL if not available.
	 */
	public default Throwable getTrace() {
		return null;
	}

	/**
	 * Returns a boolean if there is a trace of the task invocation/creation.
	 * 
	 * @since FutureTask 1.1
	 * @returns boolean.
	 */
	public default boolean hasTrace() {
		return getTrace() != null;
	}

	/* FT 1.1: Polling */

	/**
	 * Polls until the task is completed (and returns the result) or fails. <br>
	 * Uses the default polling algorithm.
	 * 
	 * @since FutureTask 1.1
	 * @returns T or null.
	 * @throws Throwable on <code>FAILED</code>.
	 */
	public default T pollUntilCompletion() {
		return pollUntilCompletion(FT_POLLING_METHOD_DEFAULT);
	}

	/**
	 * Polls until the task is completed (and returns the result) or fails.
	 * 
	 * @since FutureTask 1.1
	 * @param polling_algorithm - Selects the polling algorithm to use, else
	 *                          default.
	 * @returns T or null.
	 * @throws Throwable on <code>FAILED</code>.
	 */
	public default T pollUntilCompletion(final int polling_algorithm) {
		return this.waitTillCompletion();
	}

	/**
	 * Polls until the task is completed (and returns the result) or fails/times
	 * out.
	 * 
	 * @since FutureTask 1.1
	 * @param amount - The amount to poll of the unit.
	 * @param unit   - The time unit to use.
	 * @returns T or null.
	 * @throws Throwable on <code>FAILED</code> or time-out.
	 */
	public default T pollFor(final long amount, final TimeUnit unit) {
		return pollFor(amount, unit, FT_POLLING_METHOD_DEFAULT);
	}

	/**
	 * Polls until the task is completed (and returns the result) or fails/times
	 * out.
	 * 
	 * @since FutureTask 1.1
	 * @param amount            - The amount to poll of the unit.
	 * @param unit              - The time unit to use.
	 * @param polling_algorithm - Selects the polling algorithm to use, else
	 *                          default.
	 * @returns T or null.
	 * @throws Throwable on <code>FAILED</code> or time-out.
	 */
	public default T pollFor(final long amount, final TimeUnit unit, final int polling_algorithm) {
		return waitFor(amount, unit);
	}

	public default boolean hasExecutingThread() {
		return false;
	}

	public default Thread getExecutingThread() {
		return null;
	}

	public default String toString(final boolean disableResultPrinting) {
		return toString();
	}

	/* FT 1.2 */
}
